<template>
  <div class="pc-role-bg">
    <RemindWebSocket />
    <div class="layout-container">
      <!-- 侧边栏 -->
      <SideBar />
      <!-- 主内容区 -->
      <div class="main-content main-content-rounded">
        <div class="role-title-row main-header">
          <el-icon class="back-btn" @click="goBack">
            <ArrowLeft />
          </el-icon>
          <span class="role-title">{{ isEdit ? '编辑角色' : '创建角色' }}</span>
        </div>
        <div class="main-divider"></div>
        <!-- 加载遮罩：编辑模式获取详情时显示 -->
        <div v-if="loading" class="loading-mask">
          <div class="loading-spinner"></div>
          <div class="loading-text">正在加载...</div>
        </div>
        <div class="main-body" v-if="!isMobile">
          <el-form :model="form" label-width="0" class="role-form form-compact">
            <el-row :gutter="16">
              <el-col :span="8" :xs="8">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">姓名</span>
                </div>
                <el-input v-model="form.name" placeholder="请输入姓名" />
                <div v-if="errors.name" class="input-error">{{ errors.name }}</div>
              </el-col>
              <el-col :span="4" :xs="8">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">性别</span>
                </div>
                <el-select v-model="form.gender" placeholder="请选择性别">
                  <el-option v-for="item in dictData.gender" :key="item.dictCode || item.value || item.dictValue"
                    :label="item.dictLabel || item.label" :value="item.dictValue || item.value" />
                </el-select>
                <div v-if="errors.gender" class="input-error">{{ errors.gender }}</div>
              </el-col>
              <el-col :span="6"></el-col>
              <el-col :span="6"></el-col>
            </el-row>
            <el-row :gutter="16">
              <el-col :span="4" :xs="8">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">年龄</span>
                </div>
                <el-input v-model="form.age" placeholder="年龄" disabled />
                <div v-if="errors.age" class="input-error">{{ errors.age }}</div>
              </el-col>
              <el-col :span="4" :xs="8">
                <div class="input-label-row"><span class="input-label-star">*</span><span
                    class="input-label">出生日期</span></div>
                <el-date-picker v-model="birthDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
                  style="width:100%;" />
                <div v-if="errors.birthYear" class="input-error">{{ errors.birthYear }}</div>
              </el-col>
              <el-col :span="4" :xs="8">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">籍贯</span>
                </div>
                <el-cascader v-model="nativePlace" :options="areaOptions"
                  :props="{ checkStrictly: false, emitPath: true }" clearable filterable placeholder="选择省/市"
                  style="width:100%;" />
                <div v-if="errors.hometown" class="input-error">{{ errors.hometown }}</div>
              </el-col>
            </el-row>
            <el-row :gutter="16">
              <el-col :span="6" :xs="8">
                <div class="input-label-row"><span class="input-label-star">*</span><span
                    class="input-label">文化程度</span></div>
                <el-select v-model="form.educationLevel" placeholder="请选择">
                  <el-option label="小学及以下" value="小学及以下"></el-option>
                  <el-option label="初中" value="初中"></el-option>
                  <el-option label="高中" value="高中"></el-option>
                  <el-option label="大学" value="大学"></el-option>
                  <el-option label="研究生及以上" value="研究生及以上"></el-option>
                </el-select>
                <div v-if="errors.educationLevel" class="input-error">{{ errors.educationLevel }}</div>
              </el-col>
              <el-col :span="6" :xs="8">
                <div class="input-label-row"><span class="input-label-star">*</span><span
                    class="input-label">亲属关系</span></div>
                <el-select v-model="form.relationship" placeholder="请选择亲属关系">
                  <el-option v-for="item in dictData.relationship" :key="item.dictCode || item.value || item.dictValue"
                    :label="item.dictLabel || item.label" :value="item.dictValue || item.value" />
                </el-select>
                <div v-if="errors.relationship" class="input-error">{{ errors.relationship }}</div>
              </el-col>
              <el-col :span="6"></el-col>
              <el-col :span="6"></el-col>
            </el-row>
            <el-row :gutter="16">
              <el-col :span="12">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">居住地</span>
                </div>
                <el-input v-model="form.livecity" placeholder="请输入居住城市" />
                <div v-if="errors.livecity" class="input-error">{{ errors.livecity }}</div>
              </el-col>
            </el-row>
            <el-row :gutter="16" class="upload-row">
              <el-col :span="8" :xs="8">
                <div class="input-label-row"><span class="input-label">人物音频</span></div>
                <el-upload class="upload-box audio-fixed" drag :show-file-list="false" :http-request="onVoiceUpload"
                  :before-upload="beforeVoiceUpload" :disabled="uploadingVoice" accept="audio/*">
                  <div class="upload-icon-box">
                    <img src="/static/images/icon1.png" alt="音频图标" width="28" height="32" />
                  </div>
                  <div class="el-upload__text" style="padding-top: 20px;">
                    <template v-if="uploadingVoice">音频上传中...</template>
                    <template v-else-if="!form.voiceUrl">
                      将<strong>音频文件</strong>拖拽到此处，或<em class="upload-link">点击上传</em>
                    </template>
                    <template v-else>音频文件已上传</template>
                  </div>
                  <el-button class="upload-btn" type="primary" :loading="uploadingVoice">{{ (!uploadingVoice &&
                    form.voiceUrl) ? '重新上传' :
                    '上传/录制音频' }}</el-button>
                </el-upload>
              </el-col>
              <el-col :span="8" :xs="8">
                <div class="input-label-row"><span class="input-label">人物图像</span></div>
                <el-upload
                  :class="['upload-box', 'fileline-mode', (form.avatarUrl && !uploadingAvatar) ? 'has-file' : '']" drag
                  :show-file-list="false" :http-request="onAvatarUpload" :before-upload="beforeAvatarUpload"
                  @change="handleAvatarChange" :disabled="uploadingAvatar" accept="image/*">
                  <div v-if="!uploadingAvatar && !form.avatarUrl" class="upload-icon-box">
                    <div class="avatar-icons">
                      <img src="/static/images/icon2.png" alt="图片图标" width="28" height="32" />
                      <img src="/static/images/icon3.png" alt="视频图标" width="28" height="32" />
                    </div>
                  </div>
                  <div class="el-upload__text fileline-text"
                    :style="(!form.avatarUrl && !uploadingAvatar) ? 'padding-top:20px;' : ''">
                    <template v-if="uploadingAvatar">媒体上传中...</template>
                    <template v-else-if="!form.avatarUrl">
                      将<strong>图片/视频文件</strong>拖拽到此处，或<em class="upload-link">点击上传</em>
                    </template>
                    <template v-else>
                      <div class="file-line-preview">
                        <img :src="form.avatarUrl" alt="头像预览" class="avatar-preview" />
                        <span class="file-line-name">当前头像</span>
                      </div>
                    </template>
                  </div>
                  <el-button class="upload-btn" type="primary" :loading="uploadingAvatar">{{ (!uploadingAvatar &&
                    form.avatarUrl) ?
                    '重新上传' : '上传媒体' }}</el-button>
                </el-upload>
              </el-col>
              <el-col :span="8" style="flex: none; max-width: unset;">
              </el-col>
            </el-row>
          </el-form>
        </div>
        <div class="main-body" v-else>
          <el-form :model="form" label-width="0" class="role-form form-compact role-form-mobile">
            <el-row :gutter="16">
              <el-col :span="24">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">姓名</span>
                </div>
                <el-input v-model="form.name" placeholder="请输入姓名" />
                <div v-if="errors.name" class="input-error">{{ errors.name }}</div>
              </el-col>
            </el-row>
            <el-row :gutter="16">
              <el-col :span="8">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">性别</span>
                </div>
                <el-select v-model="form.gender" placeholder="请选择性别">
                  <el-option v-for="item in dictData.gender" :key="item.dictCode || item.value || item.dictValue"
                    :label="item.dictLabel || item.label" :value="item.dictValue || item.value" />
                </el-select>
                <div v-if="errors.gender" class="input-error">{{ errors.gender }}</div>
              </el-col>
              <el-col :span="16">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">籍贯</span>
                </div>
                <el-cascader v-model="nativePlace" :options="areaOptions"
                  :props="{ checkStrictly: false, emitPath: true }" clearable filterable placeholder="选择省/市"
                  style="width:100%;" />
                <div v-if="errors.hometown" class="input-error">{{ errors.hometown }}</div>
              </el-col>
            </el-row>
            <el-row :gutter="16">
              <el-col :span="8">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">年龄</span>
                </div>
                <el-input v-model="form.age" placeholder="年龄" disabled />
                <div v-if="errors.age" class="input-error">{{ errors.age }}</div>
              </el-col>
              <el-col :span="16">
                <div class="input-label-row"><span class="input-label-star">*</span><span
                    class="input-label">出生日期</span></div>
                <el-date-picker v-model="birthDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
                  style="width:100%;" />
                <div v-if="errors.birthYear" class="input-error">{{ errors.birthYear }}</div>
              </el-col>

            </el-row>
            <el-row :gutter="16">
              <el-col :span="24">
                <div class="input-label-row"><span class="input-label-star">*</span><span
                    class="input-label">文化程度</span></div>
                <el-select v-model="form.educationLevel" placeholder="请选择">
                  <el-option label="小学及以下" value="小学及以下"></el-option>
                  <el-option label="初中" value="初中"></el-option>
                  <el-option label="高中" value="高中"></el-option>
                  <el-option label="大学" value="大学"></el-option>
                  <el-option label="研究生及以上" value="研究生及以上"></el-option>
                </el-select>
                <div v-if="errors.educationLevel" class="input-error">{{ errors.educationLevel }}</div>
              </el-col>
              <el-col :span="24">
                <div class="input-label-row"><span class="input-label-star">*</span><span
                    class="input-label">亲属关系</span></div>
                <el-select v-model="form.relationship" placeholder="请选择亲属关系">
                  <el-option v-for="item in dictData.relationship" :key="item.dictCode || item.value || item.dictValue"
                    :label="item.dictLabel || item.label" :value="item.dictValue || item.value" />
                </el-select>
                <div v-if="errors.relationship" class="input-error">{{ errors.relationship }}</div>
              </el-col>
              <el-col :span="6"></el-col>
              <el-col :span="6"></el-col>
            </el-row>
            <el-row :gutter="16">
              <el-col :span="24">
                <div class="input-label-row"><span class="input-label-star">*</span><span class="input-label">居住地</span>
                </div>
                <el-input v-model="form.livecity" placeholder="请输入居住城市" />
                <div v-if="errors.livecity" class="input-error">{{ errors.livecity }}</div>
              </el-col>
            </el-row>
            <el-row :gutter="16" class="upload-row">
              <el-col :span="24">
                <div class="input-label-row"><span class="input-label">人物音频</span></div>
                <el-upload class="upload-box audio-fixed" drag :show-file-list="false" :http-request="onVoiceUpload"
                  :before-upload="beforeVoiceUpload" :disabled="uploadingVoice" accept="audio/*">
                  <div class="upload-icon-box">
                    <img src="/static/images/icon1.png" alt="音频图标" width="28" height="32" />
                  </div>
                  <div class="el-upload__text" style="padding-top: 20px;">
                    <template v-if="uploadingVoice">音频上传中...</template>
                    <template v-else-if="!form.voiceUrl">
                      将<strong>音频文件</strong>拖拽到此处，或<em class="upload-link">点击上传</em>
                    </template>
                    <template v-else>音频文件已上传</template>
                  </div>
                  <el-button class="upload-btn" type="primary" :loading="uploadingVoice">{{ (!uploadingVoice &&
                    form.voiceUrl) ? '重新上传' :
                    '上传/录制音频' }}</el-button>
                </el-upload>
              </el-col>
              <el-col :span="24" >
                <div class="input-label-row"><span class="input-label">人物图像</span></div>
                <el-upload
                  :class="['upload-box', 'fileline-mode', (form.avatarUrl && !uploadingAvatar) ? 'has-file' : '']" drag
                  :show-file-list="false" :http-request="onAvatarUpload" :before-upload="beforeAvatarUpload"
                  @change="handleAvatarChange" :disabled="uploadingAvatar" accept="image/*">
                  <div v-if="!uploadingAvatar && !form.avatarUrl" class="upload-icon-box">
                    <div class="avatar-icons">
                      <img src="/static/images/icon2.png" alt="图片图标" width="28" height="32" />
                      <img src="/static/images/icon3.png" alt="视频图标" width="28" height="32" />
                    </div>
                  </div>
                  <div class="el-upload__text fileline-text"
                    :style="(!form.avatarUrl && !uploadingAvatar) ? 'padding-top:20px;' : ''">
                    <template v-if="uploadingAvatar">媒体上传中...</template>
                    <template v-else-if="!form.avatarUrl">
                      将<strong>图片/视频文件</strong>拖拽到此处，或<em class="upload-link">点击上传</em>
                    </template>
                    <template v-else>
                      <div class="file-line-preview">
                        <img :src="form.avatarUrl" alt="头像预览" class="avatar-preview" />
                        <span class="file-line-name">当前头像</span>
                      </div>
                    </template>
                  </div>
                  <el-button class="upload-btn" type="primary" :loading="uploadingAvatar">{{ (!uploadingAvatar &&
                    form.avatarUrl) ?
                    '重新上传' : '上传媒体' }}</el-button>
                </el-upload>
              </el-col>
              <el-col :span="8" style="flex: none; max-width: unset;">
              </el-col>
            </el-row>
          </el-form>
        </div>
        <div class="main-divider-bottom"></div>
        <div class="form-btn-row">
          <el-button class="cancel-btn" @click="goBack">取消</el-button>
          <el-button class="confirm-btn" type="primary" :disabled="submitting" @click="onCreate">确定</el-button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, watch } from 'vue';
import { provinceAndCityData } from 'element-china-area-data';
import { useRoute } from 'vue-router';
import { go } from '@/utils/nav.js';
import { ArrowLeft } from '@element-plus/icons-vue';
import SideBar from '/pages/pc/sideBar.vue';
import { addRelativeProfile, updateRelativeProfile, getRelativeProfile, getDictData } from '@/api/pc/elderly';
// 本页自行处理文件上传（不修改 elderly.js），参照 personalInfo.vue 模式
import FileUploader from '@/utils/fileUpload.js';
import { useUserStore } from '@/store/modules/user';
import { useRelativeStore } from '@/store/modules/relative';
import RemindWebSocket from '@/components/RemindWebSocket.vue';
import { convertEmptyAndUndefinedToNull } from '@/utils/common.js';

const route = useRoute();
const userStore = useUserStore();
const relativeStore = useRelativeStore();

// 获取屏幕宽度
const screenWidth = ref(window.innerWidth)
console.log("screen", screenWidth.value)
// 是否是手机尺寸
const isMobile = computed(() =>
  screenWidth.value < 768
)
console.log("isMobile", isMobile.value)

// 将后端头像文件组装为可展示的 data URL
function buildAvatarDataUrl(profile) {
  if (!profile) return '';
  const mime = profile.mimeType || 'image/png';
  const base64 = profile.fileBase64 || '';
  if (!base64) return '';
  if (String(base64).startsWith('data:')) return base64;
  return `data:${mime};base64,${base64}`;
}

// 提取 dataURL 或纯 base64，返回 { base64, mimeType }
function extractBase64AndMime(source, fallbackMime = 'image/png') {
  if (!source) return { base64: '', mimeType: fallbackMime };
  // 情况1：对象结构 { fileBase64, mimeType }
  if (typeof source === 'object') {
    const b64 = source.fileBase64 || source.base64 || '';
    const mt = source.mimeType || fallbackMime;
    return { base64: typeof b64 === 'string' ? b64 : '', mimeType: mt };
  }
  // 情况2：data URL 字符串
  if (typeof source === 'string' && source.startsWith('data:')) {
    try {
      const comma = source.indexOf(',');
      const head = source.substring(0, comma);
      const b64 = source.substring(comma + 1);
      const mt = head.substring(head.indexOf(':') + 1, head.indexOf(';')) || fallbackMime;
      return { base64: b64, mimeType: mt };
    } catch { return { base64: '', mimeType: fallbackMime }; }
  }
  // 情况3：纯 base64 字符串
  return { base64: typeof source === 'string' ? source : '', mimeType: fallbackMime };
}

// 仅当路由携带 relativeId 判定为编辑；否则默认创建。
// 若后续需要在无参数情况下回到编辑，可通过额外 query 标记或下方条件逻辑。
const relativeId = ref(route.query.relativeId ? String(route.query.relativeId) : '');
const isEdit = computed(() => !!relativeId.value);

// 在创建模式下使用的临时ID，确保文件上传和创建亲属使用同一个ID
const tempRelativeId = ref('');

const dictData = reactive({
  gender: [],
  relationship: []
});

const form = ref({
  name: '',
  gender: '',
  age: '',
  birthYear: '', // YYYY-MM-DD
  hometown: '', // 省/市
  educationLevel: '',
  relationship: '',
  livecity: '',
  avatarUrl: '',
  voiceUrl: '',
  avatarData: null, // 存储头像文件的完整数据
  voiceData: null   // 存储音频文件的完整数据
});

// 新增：界面选择字段
const birthDate = ref('');
const nativePlace = ref([]); // [省,市]
const areaOptions = ref(provinceAndCityData || []);

// 单文件上传状态
const uploadingAvatar = ref(false);
const uploadingVoice = ref(false);

// 移除文件类型判断函数，不再需要显示不同类型的图标

function beforeAvatarUpload(file) {
  const ok = /image|video/.test(file.type);
  if (!ok) uni.showToast({ title: '仅支持图片或视频', icon: 'none' });
  const sizeMB = file.size / 1024 / 1024;
  if (sizeMB > 50) { uni.showToast({ title: '文件不能超过50MB', icon: 'none' }); return false; }
  // 立即生成本地预览（仅图片）
  try {
    if (/image\//.test(file.type)) {
      const url = URL.createObjectURL(file);
      // 释放旧的 blob URL
      try { if (form.value.avatarUrl && form.value.avatarUrl.startsWith('blob:')) URL.revokeObjectURL(form.value.avatarUrl); } catch { }
      form.value.avatarUrl = url;
    }
  } catch { }
  return ok;
}

// 选择文件变更时（Element Plus会传入上传文件信息）
function handleAvatarChange(file, fileList) {
  try {
    const raw = file?.raw || file;
    if (raw && raw.type && /image\//.test(raw.type)) {
      const url = URL.createObjectURL(raw);
      try { if (form.value.avatarUrl && form.value.avatarUrl.startsWith('blob:')) URL.revokeObjectURL(form.value.avatarUrl); } catch { }
      form.value.avatarUrl = url;
    }
  } catch (e) { console.warn('handleAvatarChange preview failed', e); }
}
function beforeVoiceUpload(file) {
  if (!/audio/.test(file.type)) { uni.showToast({ title: '仅支持音频文件', icon: 'none' }); return false; }
  const sizeMB = file.size / 1024 / 1024;
  if (sizeMB > 30) { uni.showToast({ title: '音频不能超过30MB', icon: 'none' }); return false; }
  return true;
}

async function doUpload(rawFile, kind) {
  try {
    // 根据模式准备上传参数
    if (isEdit.value) {
      // 编辑模式：使用真实的relativeId，不需要临时ID
      console.log('编辑模式文件上传 - 使用真实relativeId:', relativeId.value);
    } else {
      // 创建模式：使用已存在的临时ID，不再重新生成
      if (!tempRelativeId.value) {
        // 如果临时ID不存在，说明页面加载时没有生成，这里生成一个
        tempRelativeId.value = FileUploader.generateTempRelativeId();
        console.log('创建模式文件上传 - 页面未初始化临时ID，生成临时ID:', tempRelativeId.value);
      } else {
        console.log('创建模式文件上传 - 使用已存在的临时ID:', tempRelativeId.value);
      }
    }

    // 记录文件上传时的关键参数
    const uploadUserId = userStore.id;
    const uploadRelativeId = isEdit.value ? relativeId.value : tempRelativeId.value;
    const uploadMode = isEdit.value ? 3 : 2; // 编辑模式=3，创建模式=2
    console.log(`文件上传 - 类型: ${kind}, userId: ${uploadUserId}, relativeId: ${uploadRelativeId}, 模式: ${uploadMode} (${isEdit.value ? '编辑' : '创建'})`);

    // 使用新的文件上传工具，根据模式传入正确的参数
    const uploadResult = await FileUploader.uploadRelativeFile(
      rawFile,
      uploadUserId,
      uploadRelativeId,
      kind,
      uploadMode // 根据模式传入正确的参数
    );

    // 根据类型设置对应的URL
    if (kind === 'avatar') {
      // 预览地址
      form.value.avatarUrl = uploadResult.fileUrl || form.value.avatarUrl;
      // 始终保存提交用的base64（编辑/创建都保存）
      if (uploadResult.fileData) {
        form.value.avatarData = uploadResult.fileData;
      }
    } else if (kind === 'voice') {
      form.value.voiceUrl = uploadResult.fileUrl;
      // 只在创建模式下保存完整的文件数据
      if (!isEdit.value) {
        form.value.voiceData = uploadResult.fileData;
      }
    }
  } catch (e) {
    // 错误已在FileUploader中处理，这里不需要重复处理
    console.error('upload error', e);
  }
}

async function onAvatarUpload(option) {
  if (uploadingAvatar.value) return; uploadingAvatar.value = true;
  await doUpload(option.file, 'avatar');
  // 为避免缓存，追加时间戳参数触发预览刷新
  try {
    if (form.value.avatarUrl && !form.value.avatarUrl.startsWith('data:') && !form.value.avatarUrl.startsWith('blob:')) {
      const url = form.value.avatarUrl;
      const sep = url.includes('?') ? '&' : '?';
      form.value.avatarUrl = `${url}${sep}_ts=${Date.now()}`;
    }
  } catch { }
  uploadingAvatar.value = false; option.onSuccess && option.onSuccess({}, option.file);
}
async function onVoiceUpload(option) {
  if (uploadingVoice.value) return; uploadingVoice.value = true;
  form.value.voiceUrl = '';
  await doUpload(option.file, 'voice');
  uploadingVoice.value = false; option.onSuccess && option.onSuccess({}, option.file);
}
function removeAvatar() { form.value.avatarUrl = ''; }

// 移除文件预览相关的计算属性，不再需要显示文件名

const errors = ref({
  name: '', gender: '', age: '', birthYear: '', hometown: '', educationLevel: '', relationship: '', livecity: ''
});

const loading = ref(false);
const submitting = ref(false);

const validate = () => {
  let valid = true;
  const f = form.value;
  Object.keys(errors.value).forEach(k => errors.value[k] = '');
  if (!f.name) { errors.value.name = '姓名不能为空'; valid = false; }
  if (!f.gender) { errors.value.gender = '性别不能为空'; valid = false; }
  if (!f.age) { errors.value.age = '请选择出生日期以自动计算年龄'; valid = false; }
  if (!f.birthYear) { errors.value.birthYear = '出生年份不能为空'; valid = false; }
  if (!f.hometown) { errors.value.hometown = '籍贯不能为空'; valid = false; }
  if (!f.educationLevel) { errors.value.educationLevel = '文化程度不能为空'; valid = false; }
  if (!f.relationship) { errors.value.relationship = '亲属关系不能为空'; valid = false; }
  if (!f.livecity) { errors.value.livecity = '居住地不能为空'; valid = false; }

  // 统一长度/范围校验
  const len = (v) => (v == null ? 0 : String(v).length);
  const pushErr = (k, msg) => { if (!errors.value[k]) errors.value[k] = msg; valid = false; };
  const ageNum = Number(f.age);
  if (!Number.isFinite(ageNum) || ageNum < 0 || ageNum > 120) { pushErr('age', '年龄需为0-120之间的数字'); }
  if (len(f.name) > 10) pushErr('name', '姓名最多10个字符');
  if (len(f.gender) > 2) pushErr('gender', '性别最多2个字符');
  if (len(f.hometown) > 50) pushErr('hometown', '籍贯最多50个字符');
  if (len(f.livecity) > 50) pushErr('livecity', '居住地最多50个字符');
  if (len(f.educationLevel) > 10) pushErr('educationLevel', '文化程度最多10个字符');

  // 可选项100字符限制
  const optional100 = [];
  for (const v of optional100) {
    if (v && len(v) > 100) { valid = false; }
  }
  return valid;
};

function buildPayload() {
  const payload = {
    userId: userStore.id,
    relationship: form.value.relationship,
    name: form.value.name,
    gender: form.value.gender,
    age: String(form.value.age),
    birthDate: form.value.birthYear, // 修正：API使用 birthDate
    hometown: form.value.hometown,
    livecity: form.value.livecity,
    educationLevel: form.value.educationLevel,
  };

  // 将空字符串转换为null
  const processedPayload = convertEmptyAndUndefinedToNull(payload);

  if (isEdit.value) {
    // 编辑模式：使用原有逻辑，只传递 relativeId
    processedPayload.relativeId = Number(relativeId.value);
    // 编辑时不需要传递文件数据，保持原有逻辑
    // 如果用户上传了新文件，则传递新文件数据
    if (form.value.avatarData || form.value.avatarUrl) {
      const { base64, mimeType } = extractBase64AndMime(
        form.value.avatarData || form.value.avatarUrl,
        'image/png'
      );
      if (base64) {
        processedPayload.personalityProfile = [{
          userId: userStore.id,
          relativeId: Number(relativeId.value),
          fileType: 'IMAGE_SELF',
          fileName: 'avatar.png',
          mimeType: mimeType,
          fileBase64: base64,
          uploadTime: new Date().toISOString()
        }];
      }
    }
    if (form.value.voiceUrl) {
      processedPayload.audioRecordings = [{
        userId: userStore.id,
        relativeId: Number(relativeId.value),
        fileType: 'AUDIO_SELF',
        fileName: 'voice.mp3',
        mimeType: 'audio/mpeg',
        fileBase64: form.value.voiceUrl,
        uploadTime: new Date().toISOString()
        // 编辑模式下不设置tempRelativeId字段
      }];
    }
  } else {
    // 创建模式：使用已生成的临时ID，确保与文件上传时的ID一致
    if (!tempRelativeId.value) {
      // 如果临时ID不存在，说明页面加载时没有生成，这里生成一个
      tempRelativeId.value = FileUploader.generateTempRelativeId();
      console.log('创建亲属时生成临时ID:', tempRelativeId.value);
    } else {
      console.log('创建亲属时使用已存在的临时ID:', tempRelativeId.value);
    }

    // 设置payload的tempRelativeId
    processedPayload.tempRelativeId = tempRelativeId.value;
    console.log('buildPayload - 设置的payload.tempRelativeId:', processedPayload.tempRelativeId);

    // 记录创建角色时的关键参数
    console.log(`创建角色 - userId: ${userStore.id}, tempRelativeId: ${tempRelativeId.value}`);

    // 注意：创建模式下不传递文件数据，只传递tempRelativeId
    // 后台会根据tempRelativeId关联已上传的文件（包括默认头像）
    console.log('创建模式 - 简化入参，不包含文件数据字段');
    console.log('后台将根据tempRelativeId关联已上传的文件');
  }

  // 最终检查payload
  if (!isEdit.value) {
    console.log('buildPayload 完成 - 创建模式');
    console.log('  - payload.tempRelativeId:', processedPayload.tempRelativeId);
    console.log('  - 全局tempRelativeId.value:', tempRelativeId.value);
    console.log('  - 临时ID一致性检查:', processedPayload.tempRelativeId === tempRelativeId.value);
    console.log('  - 入参已简化，不包含文件数据字段');
    console.log('  - 后台将根据tempRelativeId关联已上传的文件');
  } else {
    // 编辑模式下的检查
    console.log('buildPayload 完成 - 编辑模式');
    console.log('编辑模式 - payload.relativeId:', processedPayload.relativeId);
    console.log('编辑模式 - 确认没有tempRelativeId字段:', !processedPayload.hasOwnProperty('tempRelativeId'));

    // 检查编辑模式下的文件数据结构
    if (processedPayload.personalityProfile && processedPayload.personalityProfile.length > 0) {
      const avatarFile = processedPayload.personalityProfile[0];
      console.log('编辑模式 - 头像文件数据结构:', {
        userId: avatarFile.userId,
        relativeId: avatarFile.relativeId,
        fileType: avatarFile.fileType,
        hasTempRelativeId: avatarFile.hasOwnProperty('tempRelativeId')
      });
    }

    if (processedPayload.audioRecordings && processedPayload.audioRecordings.length > 0) {
      const audioFile = processedPayload.audioRecordings[0];
      console.log('编辑模式 - 音频文件数据结构:', {
        userId: audioFile.userId,
        relativeId: audioFile.relativeId,
        fileType: audioFile.fileType,
        hasTempRelativeId: audioFile.hasOwnProperty('tempRelativeId')
      });
    }
  }

  return processedPayload;
}

// 默认头像处理函数已简化，直接使用文件路径


async function onCreate() {
  // 立即设置提交状态，防止重复提交
  if (submitting.value) return;
  submitting.value = true;
  
  try {
    if (!validate()) {
      submitting.value = false;
      return;
    }
    
    // 处理默认头像：如果没有上传头像，则根据性别使用默认头像
    if (!form.value.avatarUrl && (form.value.gender === '男' || form.value.gender === '女')) {
      console.log('=== 开始处理默认头像 ===');
      console.log('性别:', form.value.gender);
      console.log('临时ID:', tempRelativeId.value);
      
      // 直接从文件路径创建File对象，避免base64转换
      try {
        const defaultAvatarPath = form.value.gender === '男' ? '/static/images/p1.png' : '/static/images/p2.png';
        const defaultFileName = form.value.gender === '男' ? 'p1.png' : 'p2.png';
        
        console.log('默认头像路径:', defaultAvatarPath);
        
        // 直接fetch文件并创建File对象
        console.log('开始fetch文件...');
        const response = await fetch(defaultAvatarPath);
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const blob = await response.blob();
        console.log('文件blob大小:', blob.size, 'bytes');
        
        const defaultAvatarFile = new File([blob], defaultFileName, { type: 'image/png' });
        console.log('File对象创建成功:', defaultAvatarFile.name, defaultAvatarFile.size);
        
        // 设置显示URL
        form.value.avatarUrl = URL.createObjectURL(blob);
        console.log('预览URL设置成功');
        
        // 执行上传操作，使用临时ID上传默认头像
        console.log('开始上传默认头像...');
        await doUpload(defaultAvatarFile, 'avatar');
        console.log('默认头像上传完成');
        
        // 等待一小段时间，确保文件上传完全处理
        console.log('等待文件处理...');
        await new Promise(resolve => setTimeout(resolve, 500));
        console.log('=== 默认头像处理完成 ===');
        
      } catch (error) {
        console.error('=== 默认头像处理失败 ===');
        console.error('错误详情:', error);
        console.error('错误堆栈:', error.stack);
        // 如果处理失败，抛出错误，不继续创建角色
        throw new Error('默认头像处理失败: ' + error.message);
      }
    }
    
    const payload = buildPayload();
    if (!isEdit.value) {
      console.log('创建模式 - 简化入参检查:');
      console.log('  - 临时ID:', tempRelativeId.value);
      console.log('  - payload.tempRelativeId:', payload.tempRelativeId);
      console.log('  - 临时ID一致:', tempRelativeId.value === payload.tempRelativeId);
      console.log('  - 入参已简化，不包含文件数据字段');
    } else {
      console.log('编辑模式 - 确认使用真实relativeId:', relativeId.value);
      console.log('编辑模式 - 确认没有tempRelativeId字段:', !payload.hasOwnProperty('tempRelativeId'));
      
      // 检查编辑模式下的文件数据结构
      if (payload.personalityProfile && payload.personalityProfile.length > 0) {
        const avatarFile = payload.personalityProfile[0];
        console.log('编辑模式 - 头像文件参数:');
        console.log('  - userId:', avatarFile.userId);
        console.log('  - relativeId:', avatarFile.relativeId);
        console.log('  - 确认没有tempRelativeId字段:', !avatarFile.hasOwnProperty('tempRelativeId'));
      }
      
      if (payload.audioRecordings && payload.audioRecordings.length > 0) {
        const audioFile = payload.audioRecordings[0];
        console.log('编辑模式 - 音频文件参数:');
        console.log('  - userId:', audioFile.userId);
        console.log('  - relativeId:', audioFile.relativeId);
        console.log('  - 确认没有tempRelativeId字段:', !audioFile.hasOwnProperty('tempRelativeId'));
      }
    }
    
    // 添加调试日志，查看提交的数据结构
    console.log('提交的数据结构:', {
      isEdit: isEdit.value,
      payload: payload,
      tempRelativeId: payload.tempRelativeId
    });
    
    if (isEdit.value) {
      await updateRelativeProfile(payload);
	  uni.showToast({ title: '角色修改成功', icon: 'success' });
      // 刷新完整列表以更新头像缓存
      try { await relativeStore.fetchHomeList(userStore.id, true); } catch(e) {}
      // 返回角色列表页
      go('/pages/pc/role-edit', { replace: true });
    } else {
      console.log('=== 开始创建角色 ===');
      console.log('请求URL: /api/elderly/relative');
      console.log('请求方法: POST');
      console.log('请求payload:', JSON.stringify(payload, null, 2));
      
      try {
        const resp = await addRelativeProfile(payload);
        console.log('=== 角色创建成功 ===');
        console.log('响应数据:', resp);
        
		uni.showToast({ title: '角色创建成功', icon: 'success' });
        
        // 不管来自哪个页面，都直接跳转到首页
        // 刷新数据
        console.log('开始刷新数据...');
        await Promise.all([
          relativeStore.fetchHomeList(userStore.id, true),
          relativeStore.fetchList(userStore.id, true)
        ]);
        console.log('数据刷新完成');
        
        // 跳转到首页
        console.log('跳转到首页 - 临时ID:', tempRelativeId.value);
        go('/pages/pc/index', { replace: true });
        return;
      } catch (createError) {
        console.error('=== 角色创建失败 ===');
        console.error('错误详情:', createError);
        console.error('错误响应:', createError.response);
        throw createError;
      }
    }
  } catch (e) {
    uni.showToast({ title: (e && e.msg) || '提交失败', icon: 'none' });
    console.error('submit error', e);
  } finally {
    submitting.value = false;
  }
}

function resetForm() {
  form.value = {
    name: '',
    gender: '',
    age: '',
    birthYear: '',
    hometown: '',
    educationLevel: '',
    relationship: '',
    livecity: '',
    avatarUrl: '',
    voiceUrl: '',
    avatarData: null,
    voiceData: null
  };
  birthDate.value = '';
  nativePlace.value = [];
  Object.keys(errors.value).forEach(k => errors.value[k] = '');

  // 注意：不重置tempRelativeId，保持文件上传和创建亲属使用同一个ID
  console.log('resetForm - 保持tempRelativeId不变:', tempRelativeId.value);
}

async function loadDicts() {
  try {
    const [gd, rs] = await Promise.all([
      getDictData('gender'),
      getDictData('relationship')
    ]);
    dictData.gender = gd.data || [];
    dictData.relationship = rs.data || [];
  } catch (e) {
    console.warn('加载字典失败', e);
  }
}

async function loadDetail() {
  if (!isEdit.value) return;
  loading.value = true;
  const startTime = Date.now(); // 记录开始时间

  console.log('开始加载角色详情...');

  try {
    // 清空旧数据
    form.value.avatarUrl = '';
    form.value.voiceUrl = '';

    // 直接使用完整API，但跳过文件数据处理
    const apiStartTime = Date.now();
    const { data } = await getRelativeProfile(relativeId.value);
    const apiEndTime = Date.now();
    console.log('API请求完成，耗时:', apiEndTime - apiStartTime, 'ms');
    console.log('加载的角色数据:', data); // 调试日志

    // 只加载基本信息
    form.value.name = data.name || '';
    form.value.gender = data.gender || '';
    form.value.age = data.age != null ? String(data.age) : '';
    form.value.birthYear = data.birthDate || '';
    birthDate.value = form.value.birthYear;
    form.value.hometown = data.hometown || '';
    if (form.value.hometown) nativePlace.value = form.value.hometown.split('/');
    form.value.educationLevel = data.educationLevel || '';
    form.value.relationship = data.relationship || '';
    form.value.livecity = data.livecity || '';

    // 加载头像文件（若有）并显示预览，可在此基础上重新上传替换
    if (Array.isArray(data.personalityProfile) && data.personalityProfile.length > 0) {
      const firstProfile = data.personalityProfile.find(p => p && p.fileType === 'IMAGE_SELF') || data.personalityProfile[0];
      form.value.avatarUrl = buildAvatarDataUrl(firstProfile) || '';
    }
    // 加载音频文件（若有），用于显示“已上传”状态或播放
    if (Array.isArray(data.audioRecordings) && data.audioRecordings.length > 0) {
      try {
        const firstAudio = data.audioRecordings.find(a => a && a.fileType === 'AUDIO_SELF') || data.audioRecordings[0]
        const mime = firstAudio.mimeType || 'audio/mpeg'
        const base64 = firstAudio.fileBase64 || ''
        if (base64) {
          form.value.voiceUrl = String(base64).startsWith('data:') ? base64 : `data:${mime};base64,${base64}`
        }
      } catch (e) { console.warn('build audio preview failed', e) }
    }

    // 基本信息加载完成
    loading.value = false;
    console.log('基本信息加载完成，耗时:', Date.now() - startTime, 'ms');
    console.log('编辑模式：不自动显示文件预览，用户需要主动上传新文件');

  } catch (e) {
    uni.showToast({ title: '加载角色信息失败', icon: 'none' });
    console.error('loadDetail error', e);
    loading.value = false;
  }
}

// 返回
function goBack() {
  const from = route.query.from;
  if (isEdit.value) {
    go('/pages/pc/role-edit', { replace: true });
    return;
  }
  if (from === 'role-edit') {
    go('/pages/pc/role-edit', { replace: true });
  } else {
    go('/pages/pc/index');
  }
}

onMounted(() => {
  const mountStartTime = Date.now();

  if (!userStore.id) {
    uni.showToast({ title: '未获取到用户ID，请重新登录', icon: 'none' });
    go('/pages/pc/login', { replace: true });
    return;
  }

  // 如果没有 relativeId 代表创建，强制重置
  if (!relativeId.value) {
    resetForm();
    // 创建模式下，初始化临时ID - 这是唯一生成临时ID的地方
    tempRelativeId.value = FileUploader.generateTempRelativeId();
    console.log('页面加载时生成临时ID:', tempRelativeId.value);
    console.log('注意：此临时ID将在整个创建流程中保持一致，包括文件上传和角色创建');
  }

  // 条件回填：若没有路由参数，但存储中存在 currentRelativeId，并且没有显式 create 标记，则视为编辑再加载。
  // 可通过在创建按钮导航时添加 ?mode=create 来阻止自动回填。
  if (!relativeId.value && !route.query.mode && relativeStore.currentRelativeId) {
    relativeId.value = String(relativeStore.currentRelativeId);
  }

  // 并行加载字典数据和角色详情
  Promise.all([
    loadDicts(),
    relativeId.value ? loadDetail() : Promise.resolve()
  ]).then(() => {
    const totalTime = Date.now() - mountStartTime;
    console.log(`页面完全加载完成，总耗时: ${totalTime}ms`);

    // 创建模式下的额外检查
    if (!relativeId.value) {
      console.log('创建模式 - 临时ID状态检查:');
      console.log('  - 页面加载时生成的临时ID:', tempRelativeId.value);
      console.log('  - 临时ID将在文件上传和角色创建时保持一致');
    }
  });
});

// 出生日期 -> 自动 age & birthYear
watch(birthDate, (val) => {
  if (!val) { form.value.birthYear = ''; form.value.age = ''; return; }
  form.value.birthYear = val;
  try {
    const d = new Date(val);
    if (!isNaN(d.getTime())) {
      const now = new Date();
      let age = now.getFullYear() - d.getFullYear();
      const mdNow = now.getMonth() * 100 + now.getDate();
      const mdBirth = d.getMonth() * 100 + d.getDate();
      if (mdNow < mdBirth) age -= 1;
      form.value.age = String(age);
    }
  } catch { }
});

// 籍贯级联 -> 字符串
watch(nativePlace, (arr) => {
  if (Array.isArray(arr) && arr.length) form.value.hometown = arr.join('/'); else form.value.hometown = '';
});

// 若未来需要基于 store 切换编辑，可在路由带上 relativeId；此处移除自动同步以防误判
</script>

<style scoped>
.layout-container {
  display: flex;
  height: 100vh;
  background-color: #1976ed;
  border-radius: 0;
  overflow: hidden;
  position: relative;
}

.main-content.main-content-rounded {
  flex: 1;
  width: 864px;
  background: #fff;
  border-radius: 24px 0 0 24px;
  box-shadow: 0 4px 32px 0 rgba(227, 231, 237, 0.18);
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.loading-mask {
  position: absolute;
  inset: 64px 0 0 0;
  /* 盖住主内容但不遮住顶部标题 */
  background: rgba(255, 255, 255, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  z-index: 5;
}

.loading-mask .loading-spinner {
  width: 36px;
  height: 36px;
  border: 3px solid #f3f3f3;
  border-top: 3px solid #1976ed;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

.loading-mask .loading-text {
  margin-top: 10px;
  color: #1976ed;
  font-size: 13px;
}

.main-header {
  height: 64px;
  display: flex;
  align-items: center;
  padding-left: 24px;
  background: #fff;
  font-size: 20px;
  font-weight: 600;
  border-bottom: none;
  flex-shrink: 0;
}

.main-divider {
  border-bottom: 1.5px solid #e6eaf0;
  margin: 0 24px;
  flex-shrink: 0;
}

.main-body {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
  padding: 20px 0;
}

.main-divider-bottom {
  border-bottom: 1.5px solid #e6eaf0;
  margin: 0 24px;
  flex-shrink: 0;
}

.role-title-row {
  display: flex;
  align-items: center;
  margin-bottom: 0;
}

.back-btn {
  font-size: 32px;
  color: #222;
  cursor: pointer;
  margin-right: 18px;
  font-weight: 700;
}

.role-title {
  font-size: 32px;
  font-weight: 700;
  color: #222;
}

.role-form {
  background: #fff;
  border-radius: 12px;
  box-shadow: none;
  padding: 20px 0 0 30px;
  margin-right: 0;
}

.role-form-mobile {
  background: #fff;
  border-radius: 12px;
  box-shadow: none;
  padding: 10px;
  margin-right: 0;
}

.form-btn-row {
  display: flex;
  gap: 16px;
  background: transparent;
  padding: 24px 48px;
  justify-content: flex-end;
  flex-shrink: 0;
}

.form-compact .el-form-item {
  margin-bottom: 0;
}

.input-label-row {
  display: flex;
  align-items: center;
  font-size: 15px;
  color: #222;
  font-weight: 500;
  margin-bottom: 4px;
  margin-top: 15px;
}

.input-label-star {
  color: #ff3b30;
  font-size: 18px;
  font-weight: 700;
  margin-right: 2px;
}

.input-label {
  font-size: 15px;
  color: #222;
  font-weight: 500;
}

.upload-row {
  margin-top: 38px;
}

.upload-link {
  color: #1976ed;
  cursor: pointer;
  font-weight: 500;
}

.upload-btn {
  margin-top: 10px;
}

.upload-file-preview {
  margin-top: 8px;
  font-size: 13px;
  color: #1976ed;
}

.upload-success-text {
  color: #19c37d;
}

.audio-preview {
  white-space: nowrap;
}

.file-line-preview {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  line-height: 20px;
  padding: 6px 10px;
  background: #f7f8fa;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  width: 460px;
  max-width: 100%;
  box-sizing: border-box;
}

.avatar-icons {
  display: flex;
  gap: 12px;
  justify-content: center;
}

.upload-box.fileline-mode .el-upload-dragger {
  height: 180px;
  min-height: 180px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* 音频上传区域固定高度 */
.upload-box.audio-fixed .el-upload-dragger {
  height: 180px;
  min-height: 180px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* 有文件时保持居中，去掉顶部偏移 */
.upload-box.fileline-mode.has-file .el-upload-dragger {
  justify-content: center;
  padding-top: 0;
}

.upload-box.fileline-mode .file-line-preview {
  margin-top: 0;
}

.file-line-preview .el-icon {
  color: #909399;
}

.file-line-preview .name-text {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-line-preview .remove-btn {
  color: #f56c6c;
  cursor: pointer;
  font-size: 12px;
}

.avatar-preview {
  width: 120px;
  height: 120px;
  border-radius: 8px;
  object-fit: cover;
  border: 1px solid #e5e7eb;
}

.cancel-btn {
  background: #fff;
  color: #888;
  border: 1px solid #e3e7ed;
  border-radius: 8px;
  font-size: 16px;
  padding: 0 32px;
  height: 40px;
  cursor: pointer;
}

.confirm-btn {
  background: #1976ed;
  color: #fff;
  border-radius: 8px;
  font-size: 16px;
  padding: 0 32px;
  height: 40px;
  border: none;
  cursor: pointer;
  font-weight: 600;
}

.input-error {
  color: #ff3b30;
  font-size: 13px;
  margin-top: 4px;
  margin-bottom: 0;
}

.loading-text {
  color: #1976ed;
  font-size: 12px;
  font-style: italic;
}

.confirm-btn[disabled] {
  opacity: 0.6;
  cursor: not-allowed;
}
</style>